package com.yp.infrastructure.mdb.starter;


import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.yp.infrastructure.mdb.properties.DataSourceProperties;
import com.yp.infrastructure.mdb.properties.MultiDatasourceConfig;
import com.yp.infrastructure.mdb.properties.DatabaseProperties;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.jetbrains.annotations.NotNull;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 配置
 */
@Configuration
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({MultiDatasourceConfig.class})
public class MultipleDataSourceStarter implements ApplicationContextAware {

    private static final Logger log = LoggerFactory.getLogger(MultipleDataSourceStarter.class);

    @Resource
    private MultiDatasourceConfig multiDatasourceConfig;

    /**
     * 配置加载
     * @param applicationContext applicationContext
     * @throws BeansException BeansException.
     */
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        if (null == multiDatasourceConfig || null == multiDatasourceConfig.getDatabases()) {
            return;
        }
        log.info(multiDatasourceConfig.getDesc());
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
        for (DataSourceProperties configuration : multiDatasourceConfig.getDatabases()) {
            try {
                this.handleMultipleDatabaseConfiguration(defaultListableBeanFactory, configuration);
            } catch (BeansException be) {
                log.error(be.getLocalizedMessage());
            } catch (Exception e) {
                log.error(e.getMessage());
                System.exit(0);
            }
        }
    }


    /**
     * 单项处理数据源连接
     * @param factory factory.
     * @param dataSourceProperties mapperBindProperties.
     * @throws Exception .
     */
    private void handleMultipleDatabaseConfiguration(
            DefaultListableBeanFactory factory,
            DataSourceProperties dataSourceProperties) throws Exception {

        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDialect(new MySqlDialect());
        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        String prefix = dataSourceProperties.getName();
        log.info("loading database configurations:{}", prefix);

        //data source
        DataSource ds = getDataSource(dataSourceProperties.getItem());
        factory.registerSingleton(getBeanName(prefix, 1), ds);

        //session factory
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(ds);
        bean.setPlugins(interceptor);
        SqlSessionFactory sqlSessionFactory = bean.getObject();
        assert sqlSessionFactory != null;
        factory.registerSingleton(getBeanName(prefix, 2), sqlSessionFactory);

        //transaction manager;
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(ds);
        factory.registerSingleton(getBeanName(prefix, 3), dataSourceTransactionManager);

        // sql session template
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
        factory.registerSingleton(getBeanName(prefix, 4), sqlSessionTemplate);

    }

    /**
     * 后缀
     * @param prefix prefix
     * @param type type
     * @return .
     */
    private String getBeanName(String prefix, int type) {
        String name = "";
        switch (type) {
            case 1:
                return prefix + "Datasource";
            case 2:
                return prefix + "SqlSessionFactory";
            case 3:
                return prefix + "TransactionManager";
            case 4:
                return prefix + "SqlSessionTemplate";
        }
        return name;
    }

    /**
     * 这里可以更换JDBC驱动
     *
     * @param connection .
     * @return .
     */
    private DataSource getDataSource(DatabaseProperties connection) {
        HikariDataSource h = new HikariDataSource();
        h.setUsername(connection.getUser());
        h.setPassword(connection.getPassword());
        h.setDriverClassName(connection.getDriver());
        h.setJdbcUrl(connection.getUrl());
        h.setMaximumPoolSize(connection.getMax());
        h.setMinimumIdle(connection.getIdle());
        return h;
    }


}
